package cn.trigram.math;

/**
 * 牛顿迭代法
 */
public class DoubleNewtonItrMethod implements NewtonItrMethod<Double> {

  @Override
  public Double func(Double x) {

    throw new UnsupportedOperationException();
  }

  @Override
  public Double deriveFunc(Double x) {
    // 如果导函数存在为0的情况，默认用差商代替
    return differenceQuotient(x, x / (2d));
  }

  @Override
  public Double differenceQuotient(Double x0, Double x1) {

    double numerator   = func(x0) - (func(x1));
    double denominator = x0 - (x1);
    return numerator / (denominator);
  }

  @Override
  public Double fakePoint(Double x0, Double x1) {
    // 公式实际：x0 - f(x0)/f`(x0)，但用差商代替导数
    double numerator   = func(x1) * (x0 - (x1));
    double denominator = func(x0) - (func(x1));
    double diff        = numerator / (denominator);
    return x1 - (diff);
  }

  @Override
  public Double approximate(Double x0, Double x2, Double X) {
    // 跟伪点公式一样，只能用的点变了
    double numerator   = func(x2) * (x0 - (X));
    double denominator = func(x0) - (func(X));
    double diff        = numerator / (denominator);
    return x2 - (diff);
  }

  @Override
  public Double calc(Double x0, Double error) {
    // 原牛顿迭代公式求第一个点
    Double x1 = x0 - (func(x0) / (deriveFunc(x0)));
    // 求伪点
    Double X = fakePoint(x0, x1);
    // 求第三个点
    Double x2 = (x1 + (X)) / (2d);

    Double x3 = approximate(x0, x2, X);
//    System.out.println("第1次");
    int    n      = 2;
    Double lastX3 = x3;
    // 代入原函数小于误差值就退出
    while (!(Math.abs(func(x3)) < (error))) {
//      System.out.printf("第%d次%n", n++);
      x0 = x1;
      x1 = x2;
      X  = fakePoint(x0, x1);
      x2 = x3;
      x3 = approximate(x0, x2, X);
      if (lastX3.compareTo(x3) == 0) {
        // 防止相等但小数位又太小时继续运算
        break;
      }
      lastX3 = x3;
    }
    return x3;
  }

}
